home *** CD-ROM | disk | FTP | other *** search
/ Introduction to 3D Game …ogramming with DirectX 12 / Introduction-to-3D-Game-Programming-with-DirectX-12.ISO / Code.Textures / Chapter 23 Character Animation / SkinnedMesh / Ssao.cpp < prev    next >
C/C++ Source or Header  |  2016-03-02  |  16KB  |  468 lines

  1. //***************************************************************************************
  2. // Ssao.cpp by Frank Luna (C) 2011 All Rights Reserved.
  3. //***************************************************************************************
  4.  
  5. #include "Ssao.h"
  6. #include <DirectXPackedVector.h>
  7.  
  8. using namespace DirectX;
  9. using namespace DirectX::PackedVector;
  10. using namespace Microsoft::WRL;
  11.  
  12. Ssao::Ssao(
  13.     ID3D12Device* device,
  14.     ID3D12GraphicsCommandList* cmdList, 
  15.     UINT width, UINT height)
  16.  
  17. {
  18.     md3dDevice = device;
  19.  
  20.     OnResize(width, height);
  21.  
  22.     BuildOffsetVectors();
  23.     BuildRandomVectorTexture(cmdList);
  24. }
  25.  
  26. UINT Ssao::SsaoMapWidth()const
  27. {
  28.     return mRenderTargetWidth / 2;
  29. }
  30.  
  31. UINT Ssao::SsaoMapHeight()const
  32. {
  33.     return mRenderTargetHeight / 2;
  34. }
  35.  
  36. void Ssao::GetOffsetVectors(DirectX::XMFLOAT4 offsets[14])
  37. {
  38.     std::copy(&mOffsets[0], &mOffsets[14], &offsets[0]);
  39. }
  40.  
  41. std::vector<float> Ssao::CalcGaussWeights(float sigma)
  42. {
  43.     float twoSigma2 = 2.0f*sigma*sigma;
  44.  
  45.     // Estimate the blur radius based on sigma since sigma controls the "width" of the bell curve.
  46.     // For example, for sigma = 3, the width of the bell curve is 
  47.     int blurRadius = (int)ceil(2.0f * sigma);
  48.  
  49.     assert(blurRadius <= MaxBlurRadius);
  50.  
  51.     std::vector<float> weights;
  52.     weights.resize(2 * blurRadius + 1);
  53.  
  54.     float weightSum = 0.0f;
  55.  
  56.     for(int i = -blurRadius; i <= blurRadius; ++i)
  57.     {
  58.         float x = (float)i;
  59.  
  60.         weights[i + blurRadius] = expf(-x*x / twoSigma2);
  61.  
  62.         weightSum += weights[i + blurRadius];
  63.     }
  64.  
  65.     // Divide by the sum so all the weights add up to 1.0.
  66.     for(int i = 0; i < weights.size(); ++i)
  67.     {
  68.         weights[i] /= weightSum;
  69.     }
  70.  
  71.     return weights;
  72. }
  73.  
  74. ID3D12Resource* Ssao::NormalMap()
  75. {
  76.     return mNormalMap.Get();
  77. }
  78.  
  79. ID3D12Resource* Ssao::AmbientMap()
  80. {
  81.     return mAmbientMap0.Get();
  82. }
  83.  
  84. CD3DX12_CPU_DESCRIPTOR_HANDLE Ssao::NormalMapRtv()const
  85. {
  86.     return mhNormalMapCpuRtv;
  87. }
  88.  
  89. CD3DX12_GPU_DESCRIPTOR_HANDLE Ssao::NormalMapSrv()const
  90. {
  91.     return mhNormalMapGpuSrv;
  92. }
  93.  
  94. CD3DX12_GPU_DESCRIPTOR_HANDLE Ssao::AmbientMapSrv()const
  95. {
  96.     return mhAmbientMap0GpuSrv;
  97. }
  98.  
  99. void Ssao::BuildDescriptors(
  100.     ID3D12Resource* depthStencilBuffer,
  101.     CD3DX12_CPU_DESCRIPTOR_HANDLE hCpuSrv,
  102.     CD3DX12_GPU_DESCRIPTOR_HANDLE hGpuSrv,
  103.     CD3DX12_CPU_DESCRIPTOR_HANDLE hCpuRtv,
  104.     UINT cbvSrvUavDescriptorSize,
  105.     UINT rtvDescriptorSize)
  106. {
  107.     // Save references to the descriptors.  The Ssao reserves heap space
  108.     // for 5 contiguous Srvs.
  109.  
  110.     mhAmbientMap0CpuSrv = hCpuSrv;
  111.     mhAmbientMap1CpuSrv = hCpuSrv.Offset(1, cbvSrvUavDescriptorSize);
  112.     mhNormalMapCpuSrv = hCpuSrv.Offset(1, cbvSrvUavDescriptorSize);
  113.     mhDepthMapCpuSrv = hCpuSrv.Offset(1, cbvSrvUavDescriptorSize);
  114.     mhRandomVectorMapCpuSrv = hCpuSrv.Offset(1, cbvSrvUavDescriptorSize);
  115.  
  116.     mhAmbientMap0GpuSrv = hGpuSrv;
  117.     mhAmbientMap1GpuSrv = hGpuSrv.Offset(1, cbvSrvUavDescriptorSize);
  118.     mhNormalMapGpuSrv = hGpuSrv.Offset(1, cbvSrvUavDescriptorSize);
  119.     mhDepthMapGpuSrv = hGpuSrv.Offset(1, cbvSrvUavDescriptorSize);
  120.     mhRandomVectorMapGpuSrv = hGpuSrv.Offset(1, cbvSrvUavDescriptorSize);
  121.  
  122.     mhNormalMapCpuRtv = hCpuRtv;
  123.     mhAmbientMap0CpuRtv = hCpuRtv.Offset(1, rtvDescriptorSize);
  124.     mhAmbientMap1CpuRtv = hCpuRtv.Offset(1, rtvDescriptorSize);
  125.  
  126.     //  Create the descriptors
  127.     RebuildDescriptors(depthStencilBuffer);
  128. }
  129.  
  130. void Ssao::RebuildDescriptors(ID3D12Resource* depthStencilBuffer)
  131. {
  132.     D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
  133.     srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
  134.     srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
  135.     srvDesc.Format = NormalMapFormat;
  136.     srvDesc.Texture2D.MostDetailedMip = 0;
  137.     srvDesc.Texture2D.MipLevels = 1;
  138.     md3dDevice->CreateShaderResourceView(mNormalMap.Get(), &srvDesc, mhNormalMapCpuSrv);
  139.  
  140.     srvDesc.Format = DXGI_FORMAT_R24_UNORM_X8_TYPELESS;
  141.     md3dDevice->CreateShaderResourceView(depthStencilBuffer, &srvDesc, mhDepthMapCpuSrv);
  142.  
  143.     srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
  144.     md3dDevice->CreateShaderResourceView(mRandomVectorMap.Get(), &srvDesc, mhRandomVectorMapCpuSrv);
  145.  
  146.     srvDesc.Format = AmbientMapFormat;
  147.     md3dDevice->CreateShaderResourceView(mAmbientMap0.Get(), &srvDesc, mhAmbientMap0CpuSrv);
  148.     md3dDevice->CreateShaderResourceView(mAmbientMap1.Get(), &srvDesc, mhAmbientMap1CpuSrv);
  149.  
  150.     D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {};
  151.     rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;
  152.     rtvDesc.Format = NormalMapFormat;
  153.     rtvDesc.Texture2D.MipSlice = 0;
  154.     rtvDesc.Texture2D.PlaneSlice = 0;
  155.     md3dDevice->CreateRenderTargetView(mNormalMap.Get(), &rtvDesc, mhNormalMapCpuRtv);
  156.  
  157.     rtvDesc.Format = AmbientMapFormat;
  158.     md3dDevice->CreateRenderTargetView(mAmbientMap0.Get(), &rtvDesc, mhAmbientMap0CpuRtv);
  159.     md3dDevice->CreateRenderTargetView(mAmbientMap1.Get(), &rtvDesc, mhAmbientMap1CpuRtv);
  160. }
  161.  
  162. void Ssao::SetPSOs(ID3D12PipelineState* ssaoPso, ID3D12PipelineState* ssaoBlurPso)
  163. {
  164.     mSsaoPso = ssaoPso;
  165.     mBlurPso = ssaoBlurPso;
  166. }
  167.  
  168. void Ssao::OnResize(UINT newWidth, UINT newHeight)
  169. {
  170.     if(mRenderTargetWidth != newWidth || mRenderTargetHeight != newHeight)
  171.     {
  172.         mRenderTargetWidth = newWidth;
  173.         mRenderTargetHeight = newHeight;
  174.  
  175.         // We render to ambient map at half the resolution.
  176.         mViewport.TopLeftX = 0.0f;
  177.         mViewport.TopLeftY = 0.0f;
  178.         mViewport.Width = mRenderTargetWidth / 2.0f;
  179.         mViewport.Height = mRenderTargetHeight / 2.0f;
  180.         mViewport.MinDepth = 0.0f;
  181.         mViewport.MaxDepth = 1.0f;
  182.  
  183.         mScissorRect = { 0, 0, (int)mRenderTargetWidth / 2, (int)mRenderTargetHeight / 2 };
  184.  
  185.         BuildResources();
  186.     }
  187. }
  188.  
  189. void Ssao::ComputeSsao(
  190.     ID3D12GraphicsCommandList* cmdList,
  191.     FrameResource* currFrame, 
  192.     int blurCount)
  193. {
  194.     cmdList->RSSetViewports(1, &mViewport);
  195.     cmdList->RSSetScissorRects(1, &mScissorRect);
  196.  
  197.     // We compute the initial SSAO to AmbientMap0.
  198.  
  199.     // Change to RENDER_TARGET.
  200.     cmdList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(mAmbientMap0.Get(),
  201.         D3D12_RESOURCE_STATE_GENERIC_READ, D3D12_RESOURCE_STATE_RENDER_TARGET));
  202.   
  203.     float clearValue[] = {1.0f, 1.0f, 1.0f, 1.0f};
  204.     cmdList->ClearRenderTargetView(mhAmbientMap0CpuRtv, clearValue, 0, nullptr);
  205.      
  206.     // Specify the buffers we are going to render to.
  207.     cmdList->OMSetRenderTargets(1, &mhAmbientMap0CpuRtv, true, nullptr);
  208.  
  209.     // Bind the constant buffer for this pass.
  210.     auto ssaoCBAddress = currFrame->SsaoCB->Resource()->GetGPUVirtualAddress();
  211.     cmdList->SetGraphicsRootConstantBufferView(0, ssaoCBAddress);
  212.     cmdList->SetGraphicsRoot32BitConstant(1, 0, 0);
  213.  
  214.     // Bind the normal and depth maps.
  215.     cmdList->SetGraphicsRootDescriptorTable(2, mhNormalMapGpuSrv);
  216.  
  217.     // Bind the random vector map.
  218.     cmdList->SetGraphicsRootDescriptorTable(3, mhRandomVectorMapGpuSrv);
  219.  
  220.     cmdList->SetPipelineState(mSsaoPso);
  221.  
  222.     // Draw fullscreen quad.
  223.     cmdList->IASetVertexBuffers(0, 0, nullptr);
  224.     cmdList->IASetIndexBuffer(nullptr);
  225.     cmdList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
  226.     cmdList->DrawInstanced(6, 1, 0, 0);
  227.    
  228.     // Change back to GENERIC_READ so we can read the texture in a shader.
  229.     cmdList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(mAmbientMap0.Get(),
  230.         D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_GENERIC_READ));
  231.  
  232.     BlurAmbientMap(cmdList, currFrame, blurCount);
  233. }
  234.  
  235. void Ssao::BlurAmbientMap(ID3D12GraphicsCommandList* cmdList, FrameResource* currFrame, int blurCount)
  236. {
  237.     cmdList->SetPipelineState(mBlurPso);
  238.  
  239.     auto ssaoCBAddress = currFrame->SsaoCB->Resource()->GetGPUVirtualAddress();
  240.     cmdList->SetGraphicsRootConstantBufferView(0, ssaoCBAddress);
  241.  
  242.     for(int i = 0; i < blurCount; ++i)
  243.     {
  244.         BlurAmbientMap(cmdList, true);
  245.         BlurAmbientMap(cmdList, false);
  246.     }
  247. }
  248.  
  249. void Ssao::BlurAmbientMap(ID3D12GraphicsCommandList* cmdList, bool horzBlur)
  250. {
  251.     ID3D12Resource* output = nullptr;
  252.     CD3DX12_GPU_DESCRIPTOR_HANDLE inputSrv;
  253.     CD3DX12_CPU_DESCRIPTOR_HANDLE outputRtv;
  254.     
  255.     // Ping-pong the two ambient map textures as we apply
  256.     // horizontal and vertical blur passes.
  257.     if(horzBlur == true)
  258.     {
  259.         output = mAmbientMap1.Get();
  260.         inputSrv = mhAmbientMap0GpuSrv;
  261.         outputRtv = mhAmbientMap1CpuRtv;
  262.         cmdList->SetGraphicsRoot32BitConstant(1, 1, 0);
  263.     }
  264.     else
  265.     {
  266.         output = mAmbientMap0.Get();
  267.         inputSrv = mhAmbientMap1GpuSrv;
  268.         outputRtv = mhAmbientMap0CpuRtv;
  269.         cmdList->SetGraphicsRoot32BitConstant(1, 0, 0);
  270.     }
  271.  
  272.     cmdList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(output,
  273.         D3D12_RESOURCE_STATE_GENERIC_READ, D3D12_RESOURCE_STATE_RENDER_TARGET));
  274.  
  275.     float clearValue[] = { 1.0f, 1.0f, 1.0f, 1.0f };
  276.     cmdList->ClearRenderTargetView(outputRtv, clearValue, 0, nullptr);
  277.  
  278.     cmdList->OMSetRenderTargets(1, &outputRtv, true, nullptr);
  279.     
  280.     // Normal/depth map still bound.
  281.  
  282.  
  283.     // Bind the normal and depth maps.
  284.     cmdList->SetGraphicsRootDescriptorTable(2, mhNormalMapGpuSrv);
  285.  
  286.     // Bind the input ambient map to second texture table.
  287.     cmdList->SetGraphicsRootDescriptorTable(3, inputSrv);
  288.     
  289.     // Draw fullscreen quad.
  290.     cmdList->IASetVertexBuffers(0, 0, nullptr);
  291.     cmdList->IASetIndexBuffer(nullptr);
  292.     cmdList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
  293.     cmdList->DrawInstanced(6, 1, 0, 0);
  294.    
  295.     cmdList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(output,
  296.         D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_GENERIC_READ));
  297. }
  298.  
  299. void Ssao::BuildResources()
  300. {
  301.     // Free the old resources if they exist.
  302.     mNormalMap = nullptr;
  303.     mAmbientMap0 = nullptr;
  304.     mAmbientMap1 = nullptr;
  305.  
  306.     D3D12_RESOURCE_DESC texDesc;
  307.     ZeroMemory(&texDesc, sizeof(D3D12_RESOURCE_DESC));
  308.     texDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
  309.     texDesc.Alignment = 0;
  310.     texDesc.Width = mRenderTargetWidth;
  311.     texDesc.Height = mRenderTargetHeight;
  312.     texDesc.DepthOrArraySize = 1;
  313.     texDesc.MipLevels = 1;
  314.     texDesc.Format = Ssao::NormalMapFormat;
  315.     texDesc.SampleDesc.Count = 1;
  316.     texDesc.SampleDesc.Quality = 0;
  317.     texDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
  318.     texDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
  319.  
  320.  
  321.     float normalClearColor[] = { 0.0f, 0.0f, 1.0f, 0.0f };
  322.     CD3DX12_CLEAR_VALUE optClear(NormalMapFormat, normalClearColor);
  323.     ThrowIfFailed(md3dDevice->CreateCommittedResource(
  324.         &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
  325.         D3D12_HEAP_FLAG_NONE,
  326.         &texDesc,
  327.         D3D12_RESOURCE_STATE_COMMON,
  328.         &optClear,
  329.         IID_PPV_ARGS(&mNormalMap)));
  330.  
  331.     // Ambient occlusion maps are at half resolution.
  332.     texDesc.Width = mRenderTargetWidth / 2;
  333.     texDesc.Height = mRenderTargetHeight / 2;
  334.     texDesc.Format = Ssao::AmbientMapFormat;
  335.  
  336.     float ambientClearColor[] = { 1.0f, 1.0f, 1.0f, 1.0f };
  337.     optClear = CD3DX12_CLEAR_VALUE(AmbientMapFormat, ambientClearColor);
  338.  
  339.     ThrowIfFailed(md3dDevice->CreateCommittedResource(
  340.         &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
  341.         D3D12_HEAP_FLAG_NONE,
  342.         &texDesc,
  343.         D3D12_RESOURCE_STATE_GENERIC_READ,
  344.         &optClear,
  345.         IID_PPV_ARGS(&mAmbientMap0)));
  346.  
  347.     ThrowIfFailed(md3dDevice->CreateCommittedResource(
  348.         &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
  349.         D3D12_HEAP_FLAG_NONE,
  350.         &texDesc,
  351.         D3D12_RESOURCE_STATE_GENERIC_READ,
  352.         &optClear,
  353.         IID_PPV_ARGS(&mAmbientMap1)));
  354. }
  355.  
  356. void Ssao::BuildRandomVectorTexture(ID3D12GraphicsCommandList* cmdList)
  357. {
  358.     D3D12_RESOURCE_DESC texDesc;
  359.     ZeroMemory(&texDesc, sizeof(D3D12_RESOURCE_DESC));
  360.     texDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
  361.     texDesc.Alignment = 0;
  362.     texDesc.Width = 256;
  363.     texDesc.Height = 256;
  364.     texDesc.DepthOrArraySize = 1;
  365.     texDesc.MipLevels = 1;
  366.     texDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
  367.     texDesc.SampleDesc.Count = 1;
  368.     texDesc.SampleDesc.Quality = 0;
  369.     texDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
  370.     texDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
  371.  
  372.     ThrowIfFailed(md3dDevice->CreateCommittedResource(
  373.         &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
  374.         D3D12_HEAP_FLAG_NONE,
  375.         &texDesc,
  376.         D3D12_RESOURCE_STATE_GENERIC_READ,
  377.         nullptr,
  378.         IID_PPV_ARGS(&mRandomVectorMap)));
  379.  
  380.     //
  381.     // In order to copy CPU memory data into our default buffer, we need to create
  382.     // an intermediate upload heap. 
  383.     //
  384.  
  385.     const UINT num2DSubresources = texDesc.DepthOrArraySize * texDesc.MipLevels;
  386.     const UINT64 uploadBufferSize = GetRequiredIntermediateSize(mRandomVectorMap.Get(), 0, num2DSubresources);
  387.  
  388.     ThrowIfFailed(md3dDevice->CreateCommittedResource(
  389.         &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD),
  390.         D3D12_HEAP_FLAG_NONE,
  391.         &CD3DX12_RESOURCE_DESC::Buffer(uploadBufferSize),
  392.         D3D12_RESOURCE_STATE_GENERIC_READ,
  393.         nullptr,
  394.         IID_PPV_ARGS(mRandomVectorMapUploadBuffer.GetAddressOf())));
  395.  
  396.     XMCOLOR initData[256 * 256];
  397.     for(int i = 0; i < 256; ++i)
  398.     {
  399.         for(int j = 0; j < 256; ++j)
  400.         {
  401.             // Random vector in [0,1].  We will decompress in shader to [-1,1].
  402.             XMFLOAT3 v(MathHelper::RandF(), MathHelper::RandF(), MathHelper::RandF());
  403.  
  404.             initData[i * 256 + j] = XMCOLOR(v.x, v.y, v.z, 0.0f);
  405.         }
  406.     }
  407.  
  408.     D3D12_SUBRESOURCE_DATA subResourceData = {};
  409.     subResourceData.pData = initData;
  410.     subResourceData.RowPitch = 256 * sizeof(XMCOLOR);
  411.     subResourceData.SlicePitch = subResourceData.RowPitch * 256;
  412.  
  413.     //
  414.     // Schedule to copy the data to the default resource, and change states.
  415.     // Note that mCurrSol is put in the GENERIC_READ state so it can be 
  416.     // read by a shader.
  417.     //
  418.  
  419.     cmdList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(mRandomVectorMap.Get(),
  420.         D3D12_RESOURCE_STATE_GENERIC_READ, D3D12_RESOURCE_STATE_COPY_DEST));
  421.     UpdateSubresources(cmdList, mRandomVectorMap.Get(), mRandomVectorMapUploadBuffer.Get(),
  422.         0, 0, num2DSubresources, &subResourceData);
  423.     cmdList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(mRandomVectorMap.Get(),
  424.         D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_GENERIC_READ));
  425. }
  426.  
  427. void Ssao::BuildOffsetVectors()
  428. {
  429.     // Start with 14 uniformly distributed vectors.  We choose the 8 corners of the cube
  430.     // and the 6 center points along each cube face.  We always alternate the points on 
  431.     // opposites sides of the cubes.  This way we still get the vectors spread out even
  432.     // if we choose to use less than 14 samples.
  433.     
  434.     // 8 cube corners
  435.     mOffsets[0] = XMFLOAT4(+1.0f, +1.0f, +1.0f, 0.0f);
  436.     mOffsets[1] = XMFLOAT4(-1.0f, -1.0f, -1.0f, 0.0f);
  437.  
  438.     mOffsets[2] = XMFLOAT4(-1.0f, +1.0f, +1.0f, 0.0f);
  439.     mOffsets[3] = XMFLOAT4(+1.0f, -1.0f, -1.0f, 0.0f);
  440.  
  441.     mOffsets[4] = XMFLOAT4(+1.0f, +1.0f, -1.0f, 0.0f);
  442.     mOffsets[5] = XMFLOAT4(-1.0f, -1.0f, +1.0f, 0.0f);
  443.  
  444.     mOffsets[6] = XMFLOAT4(-1.0f, +1.0f, -1.0f, 0.0f);
  445.     mOffsets[7] = XMFLOAT4(+1.0f, -1.0f, +1.0f, 0.0f);
  446.  
  447.     // 6 centers of cube faces
  448.     mOffsets[8] = XMFLOAT4(-1.0f, 0.0f, 0.0f, 0.0f);
  449.     mOffsets[9] = XMFLOAT4(+1.0f, 0.0f, 0.0f, 0.0f);
  450.  
  451.     mOffsets[10] = XMFLOAT4(0.0f, -1.0f, 0.0f, 0.0f);
  452.     mOffsets[11] = XMFLOAT4(0.0f, +1.0f, 0.0f, 0.0f);
  453.  
  454.     mOffsets[12] = XMFLOAT4(0.0f, 0.0f, -1.0f, 0.0f);
  455.     mOffsets[13] = XMFLOAT4(0.0f, 0.0f, +1.0f, 0.0f);
  456.  
  457.     for(int i = 0; i < 14; ++i)
  458.     {
  459.         // Create random lengths in [0.25, 1.0].
  460.         float s = MathHelper::RandF(0.25f, 1.0f);
  461.         
  462.         XMVECTOR v = s * XMVector4Normalize(XMLoadFloat4(&mOffsets[i]));
  463.         
  464.         XMStoreFloat4(&mOffsets[i], v);
  465.     }
  466. }
  467.  
  468.